import logging
from datetime import datetime
from typing import List, Optional

from sqlalchemy import select

from app.admin.db import resource_crud
from app.admin.schemas.resource import ResourceCreate, ResourceUpdate
from app.db.session import AsyncSessionLocal
from app.models.resource import Resource
from app.models.role import Role, role_resource
from app.utils.date_utils import get_current_time
from app.utils.str_utils import generate_uuid_without_hyphen

# 配置日志
logger = logging.getLogger(__name__)


class ResourceService:
    """
    资源服务类，处理资源相关的业务逻辑
    """

    async def create_resource(self, resource: ResourceCreate) -> Resource:
        """创建资源"""
        resource.id = generate_uuid_without_hyphen()
        resource.created_at = get_current_time()
        resource.updated_at = resource.created_at

        async with AsyncSessionLocal() as db:
            # 检查资源名称是否已存在
            existing_resource = await self.get_resource_by_name(resource.name)
            if existing_resource:
                logger.warning(f"创建资源失败: 资源名称 [{resource.name}] 已存在")
                return None

            # 检查资源代码是否已存在
            existing_resource = await self.get_resource_by_code(resource.code)
            if existing_resource:
                logger.warning(f"创建资源失败: 资源代码 [{resource.code}] 已存在")
                return None

            logger.info(f"创建资源: {resource.name}")
            return await resource_crud.create_resource(db, resource)

    async def get_resource(self, resource_id: str) -> Optional[Resource]:
        """根据ID获取资源"""
        logger.debug(f"获取资源: resource_id={resource_id}")
        async with AsyncSessionLocal() as db:
            resource = await resource_crud.get_resource_by_id(db, resource_id)
            if not resource:
                logger.warning(f"资源不存在: resource_id={resource_id}")
            return resource

    async def get_resource_by_name(self, name: str) -> Optional[Resource]:
        """根据名称获取资源"""
        logger.debug(f"根据名称获取资源: name={name}")
        async with AsyncSessionLocal() as db:
            resource = await resource_crud.get_resource_by_name(db, name)
            if not resource:
                logger.debug(f"资源名称不存在: name={name}")
            return resource

    async def get_resource_by_code(self, code: str) -> Optional[Resource]:
        """根据代码获取资源"""
        logger.debug(f"根据代码获取资源: code={code}")
        async with AsyncSessionLocal() as db:
            resource = await resource_crud.get_resource_by_code(db, code)
            if not resource:
                logger.debug(f"资源代码不存在: code={code}")
            return resource

    async def get_resources(self, skip: int = 0, limit: int = 100, type: str = None) -> List[Resource]:
        """获取资源列表"""
        logger.debug(f"获取资源列表: skip={skip}, limit={limit}, type={type}")
        async with AsyncSessionLocal() as db:
            resources = await resource_crud.get_resources(db, skip=skip, limit=limit, type=type)
            logger.info(f"获取资源列表成功: 共{len(resources)}条记录")
            return resources

    async def update_resource(self, resource_id: str, resource_update: ResourceUpdate) -> Optional[Resource]:
        """更新资源"""
        logger.debug(f"更新资源: resource_id={resource_id}")
        
        # 设置更新时间
        resource_update_dict = resource_update.model_dump(exclude_unset=True)
        resource_update_dict["updated_at"] = datetime.now()
        resource_update = ResourceUpdate(**resource_update_dict)

        async with AsyncSessionLocal() as db:
            # 检查资源是否存在
            db_resource = await resource_crud.get_resource_by_id(db, resource_id)
            if not db_resource:
                logger.warning(f"更新资源失败: 资源 [{resource_id}] 不存在")
                return None
            
            # 如果更新名称，检查名称是否已存在
            if resource_update.name and resource_update.name != db_resource.name:
                existing_resource = await self.get_resource_by_name(resource_update.name)
                if existing_resource and existing_resource.id != resource_id:
                    logger.warning(f"更新资源失败: 资源名称 [{resource_update.name}] 已存在")
                    return None
            
            # 如果更新代码，检查代码是否已存在
            if resource_update.code and resource_update.code != db_resource.code:
                existing_resource = await self.get_resource_by_code(resource_update.code)
                if existing_resource and existing_resource.id != resource_id:
                    logger.warning(f"更新资源失败: 资源代码 [{resource_update.code}] 已存在")
                    return None
            
            updated_resource = await resource_crud.update_resource(db, resource_id, resource_update)
            logger.info(f"资源更新成功: resource_id={resource_id}")
            return updated_resource

    async def delete_resource(self, resource_id: str) -> bool:
        """删除资源"""
        logger.debug(f"删除资源: resource_id={resource_id}")
        
        # 检查资源是否存在
        resource = await self.get_resource(resource_id)
        if not resource:
            logger.warning(f"删除资源失败: 资源 [{resource_id}] 不存在")
            return False
        
        # 检查资源是否被角色引用
        roles = await self.get_resource_roles(resource_id)
        if roles and len(roles) > 0:
            role_names = ", ".join([role.name for role in roles])
            logger.warning(f"删除资源失败: 资源被角色引用, resource_id={resource_id}, roles={role_names}")
            return False
        
        async with AsyncSessionLocal() as db:
            result = await resource_crud.delete_resource(db, resource_id)
            if result:
                logger.info(f"资源删除成功: resource_id={resource_id}")
            else:
                logger.warning(f"资源删除失败: resource_id={resource_id}")
            return result

    async def get_resource_roles(self, resource_id: str) -> List[Role]:
        """
        获取资源关联的角色列表
        """
        logger.debug(f"获取资源关联的角色列表: resource_id={resource_id}")
        async with AsyncSessionLocal() as db:
            # 使用显式查询获取资源的角色列表
            stmt = select(Role).join(role_resource).where(role_resource.c.resource_id == resource_id)
            result = await db.execute(stmt)
            roles = result.scalars().all()
            logger.info(f"获取资源关联的角色列表成功: resource_id={resource_id}, 共{len(roles)}条记录")
            return roles

    async def check_resource_exists(self, name: str = None, code: str = None) -> bool:
        """检查资源名称或代码是否存在"""
        if name:
            resource = await self.get_resource_by_name(name)
            return resource is not None
        elif code:
            resource = await self.get_resource_by_code(code)
            return resource is not None
        return False


# 创建全局资源服务实例
resource_service = ResourceService()